//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#pragma once

#include "UI/MetaData/Meta.Types.h"

#ifdef ENGINE_META_DATA
#	include "UI/Widgets/Widget.h"
#	include "UI/Widgets/Frame.h"
#	include "UI/Widgets/Label.h"
#	include "UI/Widgets/TextBox.h"
#	include "UI/Widgets/Button.h"
#	include "UI/Widgets/Picture.h"
#	include "UI/Widgets/Group.h"
#	include "UI/Widgets/RadioButton.h"
#	include "UI/Widgets/GroupSwitch.h"
#	include "UI/Widgets/Panel.h"
#endif



	//
	// Widget Layout
	//
#ifdef ENGINE_META_DATA

	namespace MetaData
	{
		template <>
		inline void MetaDataUtils::Copy<> (ETextAlign::type &left, int &right, const EngineSubSystemsRef ess)
		{
			left = (EDock::type) right;
		}
	}

#endif

#	define META_OBJECT			WidgetLayoutCfg
#	define META_OBJECT_NAME		TOSTRING( WidgetLayout )
#	define META_NAME			MetaWidgetLayout
#	define META_BASECLASS		WidgetLayout

#	define META_FIELDS( _decl ) \
		_decl( vec2,		pos,			pos,			vec2() ) \
		_decl( vec2,		size,			size,			vec2() ) \
		_decl( rect,		margin,			margin,			rect() ) \
		_decl( int,			dock,			dock,			0 ) \
		_decl( int,			align,			align,			0 ) \
		_decl( int,			measureAlign,	measureAlign,	0x22 ) \
		_decl( int,			measure,		measure,		0 ) \
		_decl( bool,		enabled,		enabled,		false )
			
#	include "Base/MetaData/MetaDataObject.h"



	namespace MetaData
	{
		struct WidgetsVarArray
		{
		// types
		public:
			typedef variant_array_t::value_t	value_t;

		// variables
		private:
			variant_array_t	_value;

		// methods
		public:
			variant_array_t &		operator *  ()			{ return _value; }
			variant_array_t const &	operator *  ()	const	{ return _value; }

			static bool IsMetaDataInitialized ()
			{
				static bool	is_init = false;
				bool	res = is_init;
				is_init = true;
				return res;
			}
		};
		

#	ifdef EXTERNAL_META_DATA
		inline UX_DECLARE_META_TYPEDEF( WidgetsVarArray, variant_array_t );
#	endif


#	ifdef ENGINE_META_DATA

		void MetaDataUtils_CopyWidgetsVarArray (Widget::widget_array_t &left, variant_array_t &right, const EngineSubSystemsRef ess);
		bool MetaDataUtils_LoadWidgetsVarArray (const RFilePtr &file, variant_array_t &value);


		template <>
		inline void MetaDataUtils::Copy<> (Widget::widget_array_t &left, WidgetsVarArray &right, const EngineSubSystemsRef ess)
		{
			MetaDataUtils_CopyWidgetsVarArray( left, *right, ess );
		}

		template <>
		inline bool MetaDataUtils::Load<> (const RFilePtr &file, WidgetsVarArray &value)
		{
			return MetaDataUtils_LoadWidgetsVarArray( file, *value );
		}

#	endif

		
		bool MetaDataUtils_SaveWidgetsVarArray (const WFilePtr &file, const variant_array_t &value);
		void MetaDataUtils_InitWidgetsVarArrayMetaData ();
		
		template <>
		inline bool MetaDataUtils::Save<> (const WFilePtr &file, const WidgetsVarArray &value)
		{
			return MetaDataUtils_SaveWidgetsVarArray( file, *value );
		}
		
		template <>
		inline void MetaDataUtils::InitMetaData<> (const WidgetsVarArray &obj)
		{
			if ( not WidgetsVarArray::IsMetaDataInitialized() )
			{
				MetaDataUtils_InitWidgetsVarArrayMetaData();
			}
		}
	}
	


	//
	// Widget
	//
#	define META_OBJECT			WidgetCfg
#	define META_OBJECT_NAME		TOSTRING( Widget )
#	define META_NAME			MetaWidget
#	define META_BASECLASS		Widget

#	define META_FIELDS( _decl ) \
		_decl( WidgetLayoutCfg,		layout_vertical,	_layouts[ ELayout::VERTICAL ],		) \
		_decl( WidgetLayoutCfg,		layout_horizontal,	_layouts[ ELayout::HORIZONTAL ],	) \
		_decl( string,				name,				_name,								) \
		_decl( bool,				visible,			_visible,							true ) \
		_decl( bool,				enabled,			_enabled,							true ) \
		_decl( WidgetsVarArray,		widgets,			_attachedWidgets,					) \
		_decl( int,					layoutMask,			_layoutMask,						0xFF )

			
#	include "Base/MetaData/MetaDataObject.h"



	//
	// Frame
	//
#	define META_OBJECT			FrameCfg
#	define META_OBJECT_NAME		TOSTRING( Frame )
#	define META_NAME			MetaFrame
#	define META_BASECLASS		Frame
#	define META_OBJECT_PARENT	WidgetCfg

#	define META_FIELDS( _decl ) \
		_decl( bool,			overlay,			_overlay,			true ) \
		_decl( bool,			exclusiveInput,		_exclusiveInput,	true ) \
		_decl( color4u,			color,				_color,				) \
		_decl( UIMaterialCfg,	material,			_material,			) \
		_decl( vec2,			designResolution,	_designResolution,	) \
		_decl( int,				designMeasure,		_designMeasure,		0 /*EMeasure::AUTO*/ ) \
		_decl( UIActionCfg,		onClose,			_onClose,			)

#	include "Base/MetaData/MetaDataObject.h"



	//
	// Label
	//
#	define META_OBJECT			LabelCfg
#	define META_OBJECT_NAME		TOSTRING( Label )
#	define META_NAME			MetaLabel
#	define META_BASECLASS		Label
#	define META_OBJECT_PARENT	WidgetCfg

#	define META_FIELDS( _decl ) \
		_decl( UIImageCfg,			image,			_image,			) \
		_decl( UIFontCfg,			font,			_font,			) \
		_decl( ExtLocaleStringCfg,	text,			_text,			) \
		_decl( int,					textAlign,		_textAlign,		) \
		_decl( float,				textSize,		_textSize,		) \
		_decl( float,				minTextSize,	_minMaxTextSize.x,	) \
		_decl( float,				maxTextSize,	_minMaxTextSize.y,	) \
		_decl( rect,				textMargin,		_textMargin,	)

#	include "Base/MetaData/MetaDataObject.h"


	
	//
	// TextBox
	//
#	define META_OBJECT			TextBoxCfg
#	define META_OBJECT_NAME		TOSTRING( TextBox )
#	define META_NAME			MetaTextBox
#	define META_BASECLASS		TextBox
#	define META_OBJECT_PARENT	WidgetCfg

#	define META_FIELDS( _decl ) \
		_decl( UIImageCfg,			image,			_image,			) \
		_decl( UIFontCfg,			font,			_font,			) \
		_decl( ExtLocaleStringCfg,	text,			_text,			) \
		_decl( float,				textSize,		_textSize,		) \
		_decl( rect,				textMargin,		_textMargin,	) \
		_decl( float,				minTextSize,	_minMaxTextSize.x,	) \
		_decl( float,				maxTextSize,	_minMaxTextSize.y,	)

#	include "Base/MetaData/MetaDataObject.h"

	

	//
	// Button
	//
#	define META_OBJECT			ButtonCfg
#	define META_OBJECT_NAME		TOSTRING( Button )
#	define META_NAME			MetaButton
#	define META_BASECLASS		Button
#	define META_OBJECT_PARENT	LabelCfg

#	define META_FIELDS( _decl ) \
		_decl( UIActionCfg,		on_click,			_onClick,		) \
		_decl( bool,			checkedState,		_checkedState,	false ) \
		_decl( bool,			checked,			_checked,		false )

#	include "Base/MetaData/MetaDataObject.h"

	

	//
	// Picture
	//
#	define META_OBJECT			PictureCfg
#	define META_OBJECT_NAME		TOSTRING( Picture )
#	define META_NAME			MetaPicture
#	define META_BASECLASS		Picture
#	define META_OBJECT_PARENT	WidgetCfg

#	define META_FIELDS( _decl ) \
		_decl( UIImageCfg,		image,			_image,			)

#	include "Base/MetaData/MetaDataObject.h"

	

	//
	// Group
	//
#	define META_OBJECT			GroupCfg
#	define META_OBJECT_NAME		TOSTRING( Group )
#	define META_NAME			MetaGroup
#	define META_BASECLASS		Group
#	define META_OBJECT_PARENT	PictureCfg

#	define META_FIELDS( _decl )

#	include "Base/MetaData/MetaDataObject.h"

	

	//
	// Radio Button
	//
#	define META_OBJECT			RadioButtonCfg
#	define META_OBJECT_NAME		TOSTRING( RadioButton )
#	define META_NAME			MetaRadioButton
#	define META_BASECLASS		RadioButton
#	define META_OBJECT_PARENT	LabelCfg

#	define META_FIELDS( _decl ) \
		_decl( bool,			checked,			_checked,			false ) \
		_decl( UIActionCfg,		onClick,			_onClick,			) \
		_decl( UIActionCfg,		checkedChanged,		_checkedChanged,	)

#	include "Base/MetaData/MetaDataObject.h"

	

	//
	// Group Switch
	//
#	define META_OBJECT			GroupSwitchCfg
#	define META_OBJECT_NAME		TOSTRING( GroupSwitch )
#	define META_NAME			MetaGroupSwitch
#	define META_BASECLASS		GroupSwitch
#	define META_OBJECT_PARENT	WidgetCfg

#	define META_FIELDS( _decl )

#	include "Base/MetaData/MetaDataObject.h"

	

	//
	// Panel
	//
#	define META_OBJECT			PanelCfg
#	define META_OBJECT_NAME		TOSTRING( Panel )
#	define META_NAME			MetaPanel
#	define META_BASECLASS		Panel
#	define META_OBJECT_PARENT	WidgetCfg

#	define META_FIELDS( _decl ) \
		_decl( UIActionCfg,		onDraw,		_onDraw,	) \
		_decl( UIActionCfg,		onUpdate,	_onUpdate,	)

#	include "Base/MetaData/MetaDataObject.h"




	namespace MetaData
	{
		//
		// All Widgets Type List
		//
		typedef CompileTime::TypeList20< WidgetCfg, FrameCfg, LabelCfg, TextBoxCfg, ButtonCfg, PictureCfg, GroupCfg,
										 RadioButtonCfg, GroupSwitchCfg, PanelCfg >::type		WidgetsTypeList_t;


		//
		// Widgets Variant Array Element Header
		//
		struct WidgetsVarArray_ElemHeader
		{
		// types
		public:
			typedef WidgetsVarArray::value_t	value_t;

		// variables
		public:
			static_array< char, 32 >	name;
			usize						dataSize;
		
		// methods
		public:
			WidgetsVarArray_ElemHeader () : dataSize(0)
			{}

			WidgetsVarArray_ElemHeader (const value_t &value) : dataSize(0)
			{
				const uni_c_string	str = ( (WidgetCfg *) value.GetPtr() )->GetTypeName();

				ASSERT( str.Count() < name.Count() );

				dataSize = value.GetSizeOf();
				name.Copy( uni_array<const char>( str.ptr(), str.Count() ) );
			}

			bool Load (const RFilePtr &file)
			{
				return	MetaDataUtils::Load( file, dataSize ) and
						MetaDataUtils::Load( file, name );
			}

			bool Save (const WFilePtr &file) const
			{
				return	MetaDataUtils::Save( file, dataSize ) and
						MetaDataUtils::Save( file, name );
			}
		};


#	ifdef ENGINE_META_DATA
		
		//
		// Init Widgets Meta Data
		//
		struct MetaDataUtils_CreateWidget_Func
		{
		// variables
		private:
			variant_array_t::value_t	&	value;
			WidgetPtr					&	widget;
			EngineSubSystemsRef				ess;
			bool							found;

		// methods
		public:
			MetaDataUtils_CreateWidget_Func
				(variant_array_t::value_t &value, WidgetPtr &widget, const EngineSubSystemsRef ess) :
				value(value), widget(widget), ess(ess), found(false)
			{
			}

			template <typename T, usize Index>
			void Process ()
			{
				if ( found )
					return;

				if ( not value.IsDefined() )
				{
					WARNING( "value is empty!" );
					return;
				}

				typedef T						cfg_type;
				typedef typename T::object_t	type;

				typedef UX_STL::UXTypesExt::CRefCounter< type,
							UX_STL::UXTypesExt::CReferenced,
							UX_STL::UXTypesExt::TRefCountStrategy< UX_STL::UXTypesExt::CReferenced > >	typePtr;

				//const uni_c_string	base_name	= ( (WidgetCfg *) value.GetPtr() )->GetTypeName();
				//const uni_c_string	cur_name	= cfg_type::GetTypeNameStatic();

				const TypeId_t	base_type_id	= value.GetValueTypeId();
				const TypeId_t	cur_type_id		= TypeId< cfg_type >();

				if ( base_type_id == cur_type_id )
				//if ( base_name == cur_name )
				{
					cfg_type &	cfg = value.Get< cfg_type >();
					typePtr		wgt = type::New( ess );

					cfg.Init( wgt.ptr(), ess );
					
					widget = wgt;

					found = true;
				}
			}
		};

		// CreateWidget
		inline bool MetaDataUtils_CreateWidget (variant_array_t::value_t &value, WidgetPtr &widget, const EngineSubSystemsRef ess)
		{
			widget = null;

			MetaDataUtils_CreateWidget_Func	func( value, widget, ess );
			
			WidgetsTypeList_t::ForEach( func );

			if ( widget.IsNotNull() )
				return true;

			WARNING( "!!!" );
			return false;

#			undef CREATE_WIDGET
		}

		// CopyWidgetsVarArray
		inline void MetaDataUtils_CopyWidgetsVarArray (Widget::widget_array_t &left, variant_array_t &right, const EngineSubSystemsRef ess)
		{
			FOR( i, right )
			{
				WidgetPtr	wgt;

				if ( MetaDataUtils_CreateWidget( right[i], wgt, ess ) )
				{
					left.PushBack( wgt );
				}
			}
		}
		

		//
		// Load Widget
		//
		struct MetaDataUtils_LoadWidgetsVarArray_Func
		{
		// variables
		private:
			variant_array_t	&			_varArray;
			RFilePtr					_file;
			WidgetsVarArray_ElemHeader	_header;
			usize						_i;
			bool						_loaded;
			uni_c_string				_name;

		// methods
		public:
			MetaDataUtils_LoadWidgetsVarArray_Func (variant_array_t	&va, const RFilePtr &file) :
				_varArray(va), _file(file), _i(0)
			{}

			bool Load ()
			{
				FOR( i, _varArray )
				{
					_i = i;
					_loaded = false;
					_header.Load( _file );
					_name = _header.name.ptr();

					WidgetsTypeList_t::ForEach( *this );
					CHECK_ERR( _loaded );
				}
				return true;
			}

			template <typename T, usize Index>
			void Process ()
			{
				const uni_c_string	type_name = T::GetTypeNameStatic();

				if ( _name == type_name )
				{
					//ASSERT( _header.dataSize == sizeof(T) );

					_varArray[_i] = T();

					T &	wgt = _varArray[_i].Get<T>();

					wgt.Deserialize( _file );

					_loaded = true;
				}
			}
		};

		// LoadWidgetsVarArray
		inline bool MetaDataUtils_LoadWidgetsVarArray (const RFilePtr &file, variant_array_t &value)
		{
			FILE_RW_BEGIN();

			uint	count = 0;

			FILE_RW_CHECK( file->Read( count ) );
			value.Resize( count, false );

			MetaDataUtils_LoadWidgetsVarArray_Func	func( value, file );

			FILE_RW_CHECK( func.Load() );
			
			FILE_RW_RETURN();
		}
#	endif

		// SaveWidgetsVarArray
		inline bool MetaDataUtils_SaveWidgetsVarArray (const WFilePtr &file, const variant_array_t &value)
		{
			FILE_RW_BEGIN();

			const uint	count = value.Count();
			FILE_RW_CHECK( file->Write( count ) );

			FOR( i, value )
			{
				if ( not value[i].IsDefined() )
					continue;

				WidgetsVarArray_ElemHeader	hdr( value[i] );

				FILE_RW_CHECK( hdr.Save( file ) );

				FILE_RW_CHECK( ((WidgetCfg *) value[i].GetPtr() )->Serialize( file ) );
			}
			
			FILE_RW_RETURN();
		}
		

		//
		// Init Widgets Meta Data
		//
		struct MetaDataUtils_InitWidgetsVarArrayMetaData_Func
		{
			template <typename T, usize Index>
			void Process () const
			{
				T t;
				MetaDataUtils::InitMetaData( t );
			}
		};


		// InitWidgetsVarArrayMetaData
		inline void MetaDataUtils_InitWidgetsVarArrayMetaData ()
		{
			MetaDataUtils_InitWidgetsVarArrayMetaData_Func	func;

			WidgetsTypeList_t::ForEach( func );
		}
	}
